package com.plian.system.service.wf;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.plian.Tools.StringUtil;
import com.plian.Tools.TokenUtil;
import com.plian.Tools.pagedata.PageData;
import com.plian.flow.mapper.FlowOrgMapper;
import com.plian.flow.mapper.FlowSysUserMapper;
import com.plian.flow.mapper.FlowWfGroupMapper;
import com.plian.flow.mapper.FlowWfGroupRelationMapper;
import com.plian.flow.model.FlowOrg;
import com.plian.flow.model.FlowUser;
import com.plian.flow.model.FlowWfGroup;
import com.plian.flow.model.FlowWfGroupRelation;
import com.plian.system.bean.wf.UserPOJO;
import com.plian.system.bean.wf.UserTaskPOJO;
import com.plian.system.config.exception.MyRuntimeException;
import com.plian.system.helper.wf.SimulationHelper;
import com.plian.system.helper.wf.WorkFlowHelper;
import com.plian.system.model.ObjResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.impl.el.ExpressionManager;
import org.flowable.common.engine.impl.el.VariableContainerWrapper;
import org.flowable.common.engine.impl.util.CollectionUtil;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.ui.modeler.domain.ApprovalPoints;
import org.flowable.ui.modeler.service.ApprovalPointsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author：zsk
 * @Package：com.plian.system.service.wf
 * @Project：xt-gzw-20250227
 * @name：SimulationHelperTwo
 * @Date：2025/3/7 8:55
 * @Filename：SimulationHelperTwo
 */
@Service
public class SimulationHelperTwo {
    private static final Logger log = LoggerFactory.getLogger(SimulationHelper.class);
    @Autowired(
            required = false
    )
    private RepositoryService repositoryService;
    @Resource
    private ApprovalPointsService approvalPointsService;
    @Autowired(
            required = false
    )
    private SpringProcessEngineConfiguration springProcessEngineConfiguration;
    @Autowired(
            required = false
    )
    private RuntimeService runtimeService;
    @Resource
    private WorkFlowHelper workFlowHelper;
    @Resource
    private FlowOrgMapper flowOrgMapper;
    @Resource
    private FlowSysUserMapper userMapper;
    @Resource
    private FlowWfGroupRelationMapper flowWfGroupRelationMapper;
    @Resource
    private FlowWfGroupMapper flowWfGroupMapper;

    public SimulationHelperTwo() {
    }

    public LinkedList<UserTaskPOJO> simulation(ProcessInstance processInstance, Map<String, Object> processVariables, boolean saveIntoDataBase) {
        BpmnModel bpmnModel = this.repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        LinkedList<UserTaskPOJO> userTaskPOJOList = new LinkedList();
        if (processVariables == null || processVariables.isEmpty()) {
            processVariables = this.runtimeService.getVariables(processInstance.getId());
        }

        Process processes = (Process)bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = processes.getFlowElements();
        LinkedList<FlowElement> flowElementList = new LinkedList();
        Iterator var9 = flowElements.iterator();

        while(var9.hasNext()) {
            FlowElement flowElement = (FlowElement)var9.next();
            if (flowElement instanceof StartEvent) {
                this.travelBpmnModel(flowElementList, flowElement, processVariables, processInstance.getId(), processInstance.getProcessDefinitionId());
            }
        }

        List<String> flowElementIds = (List)flowElementList.stream().map(BaseElement::getId).collect(Collectors.toList());
        List<ApprovalPoints> approvalPointsList = this.approvalPointsService.getByUserTaskIdsOrderBySerialNumber(flowElementIds, processInstance.getProcessDefinitionKey());
        Map<String, List<ApprovalPoints>> approvalPointsListMap = (Map)((Stream)approvalPointsList.stream().parallel()).collect(Collectors.groupingBy(ApprovalPoints::getUserTaskResourceId));
        boolean isFirstTask = true;
        String currentNodeOrganization = null;
        String unitOrgId = processVariables.get("unitOrgId") != null ? String.valueOf(processVariables.get("unitOrgId")) : "";

        for(int i = 0; i < flowElementList.size(); ++i) {
            FlowElement next = (FlowElement)flowElementList.get(i);
            if (next instanceof UserTask) {
                UserTask userTask = (UserTask)next;
                UserTaskPOJO info = new UserTaskPOJO();
                info.setTaskDefineId(userTask.getId());
                List approvalPoints;
                if (isFirstTask) {
                    info.setTaskState(5);
                    FlowUser flowUser = (FlowUser)this.userMapper.selectById(TokenUtil.getTokenUserId());
                    UserPOJO userPOJO = new UserPOJO(flowUser.getId(), flowUser.getName());
                    info.setUserPOJO(userPOJO);
                    info.setBelongedOrganizationId((String)processVariables.get("organizationId"));
                    currentNodeOrganization = (String)processVariables.get("organizationId");
                    isFirstTask = false;
                } else {
                    info.setTaskState(0);
                    approvalPoints = userTask.getCandidateGroups();
                    String assignee = userTask.getAssignee();
                    if (CollectionUtil.isNotEmpty(approvalPoints) && null == assignee) {
                        String groupId = (String)approvalPoints.get(0);
                        FlowOrg current = (FlowOrg)this.flowOrgMapper.selectById(currentNodeOrganization);
                        if (current == null) {
                            throw new MyRuntimeException(new ObjResult("流程模拟时，获取当前节点单位失败！"));
                        }

                        if (!"0".equals(current.getParentId()) && !userTask.getName().contains("同级")) {
                            FlowOrg parent = this.findRealParentNode(current);
                            currentNodeOrganization = parent.getId();
                        }

                        if (userTask.getName().contains("集团")) {
                            currentNodeOrganization = unitOrgId;
                        }
                        if(i==flowElementList.size()-1){//设定死最后一步是去国资委找人
                            currentNodeOrganization= TokenUtil.getCompetentOrganization();//  "b7199ac7f4bd453782446f47d5e8d3a0";
                        }


                        PageData pageData = new PageData();
                        pageData.put("groupId", groupId);
                        pageData.put("organizationId", currentNodeOrganization);
                        List<FlowWfGroupRelation> relationList = this.flowWfGroupRelationMapper.selectList(
                                new LambdaQueryWrapper<FlowWfGroupRelation>().eq(FlowWfGroupRelation::getGroupId, groupId).eq(FlowWfGroupRelation::getOrganizationId, currentNodeOrganization));
                        if (CollectionUtil.isEmpty(relationList)) {
                            FlowWfGroup flowWfGroup = (FlowWfGroup)this.flowWfGroupMapper.selectById(groupId);
                            throw new MyRuntimeException(new ObjResult(StringUtil.format("{},未找到用户，请联系管理员，检查{}节点的{}下是否分配人员", new Object[]{userTask.getName(), current.getName(), flowWfGroup != null ? flowWfGroup.getName() : ""})));
                        }

                        List<FlowUser> flowUserList = this.userMapper.selectBatchIds((Collection)relationList.stream().map(FlowWfGroupRelation::getUserId).collect(Collectors.toList()));
                        info.setBelongedOrganizationId(currentNodeOrganization);
                        Iterator var26 = flowUserList.iterator();

                        while(var26.hasNext()) {
                            FlowUser flowUser = (FlowUser)var26.next();
                            info.setUserPOJO(new UserPOJO(flowUser.getId(), flowUser.getName()));
                        }
                    } else if (StringUtils.isNotEmpty(assignee) && CollectionUtils.isEmpty(approvalPoints)) {
                        switch (assignee) {
                            case "${applicantId}":
                                String applicantId = (String)processVariables.getOrDefault("applicantId", (Object)null);
                                if (applicantId != null) {
                                    FlowUser applicantFlowUser = (FlowUser)this.userMapper.selectById(applicantId);
                                    info.setUserPOJO(new UserPOJO(applicantId, applicantFlowUser.getName()));
                                }
                                break;
                            default:
                                if (assignee != null) {
                                    FlowUser assigneeFlowUser = (FlowUser)this.userMapper.selectById(assignee);
                                    info.setUserPOJO(new UserPOJO(assignee, assigneeFlowUser.getName()));
                                }
                        }
                    } else if (StringUtils.isNotEmpty(userTask.getAssignee()) && !userTask.getAssignee().startsWith("$")) {
                        FlowUser assigneeFlowUser = (FlowUser)this.userMapper.selectById(userTask.getAssignee());
                        info.setUserPOJO(new UserPOJO(userTask.getAssignee(), assigneeFlowUser.getName()));
                    }
                }

                info.setTaskName(userTask.getName());
                approvalPoints = (List)approvalPointsListMap.get(userTask.getId());
                if (approvalPoints == null) {
                    info.setApprovalPointsList(Collections.emptyList());
                } else {
                    approvalPoints.sort(Comparator.comparing((o) -> {
                        return Integer.valueOf(String.valueOf(o));
                    }));
                    info.setApprovalPointsList(approvalPoints);
                }

                userTaskPOJOList.add(info);
            }
        }

        if (saveIntoDataBase) {
            this.workFlowHelper.saveUserTaskListIntoVariable(userTaskPOJOList, processInstance.getId());
        }

        return userTaskPOJOList;
    }

    private FlowOrg findRealParentNode(FlowOrg current) {
        try {
            FlowOrg parent = (FlowOrg)this.flowOrgMapper.selectById(current.getParentId());
            return parent.getType() != null && parent.getType() == 3 ? this.findRealParentNode(parent) : parent;
        } catch (Exception var3) {
            log.error(ExceptionUtils.getFullStackTrace(var3));
            throw new MyRuntimeException(new ObjResult("获取单位父节点异常！"));
        }
    }

    private void travelBpmnModel(LinkedList<FlowElement> linkedList, FlowElement flowElement, Map<String, Object> variable, String processInstanceId, String processDefineId) {
        if (!(flowElement instanceof EndEvent)) {
            if (flowElement instanceof FlowNode) {
                FlowNode flowNode = (FlowNode)flowElement;
                List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
                if (CollectionUtil.isEmpty(outgoingFlows)) {
                    return;
                }

                FlowElement targetFlowElement;
                if (outgoingFlows.size() == 1) {
                    targetFlowElement = ((SequenceFlow)outgoingFlows.get(0)).getTargetFlowElement();
                } else {
                    targetFlowElement = this.chooseOutgoingFlow(flowNode, variable, processInstanceId, processDefineId);
                }

                if (targetFlowElement instanceof UserTask) {
                    linkedList.add(targetFlowElement);
                }

                this.travelBpmnModel(linkedList, targetFlowElement, variable, processInstanceId, processDefineId);
            }

        }
    }

    private FlowElement chooseOutgoingFlow(FlowNode flowNode, Map<String, Object> processVariables, String processInstanceId, String processDefineId) {
        List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
        if (flowNode instanceof ExclusiveGateway) {
            ExpressionManager expressionManager = this.springProcessEngineConfiguration.getExpressionManager();
            Iterator var7 = outgoingFlows.iterator();

            while(var7.hasNext()) {
                SequenceFlow flow = (SequenceFlow)var7.next();
                String expression = flow.getConditionExpression();
                if (StringUtils.isEmpty(expression)) {
                    return flow.getTargetFlowElement();
                }

                VariableContainerWrapper variable = new VariableContainerWrapper(processVariables);
                Expression expressionManagerExpression = expressionManager.createExpression(expression);

                try {
                    Object value = expressionManagerExpression.getValue(variable);
                    if (!(value instanceof Boolean)) {
                        throw new MyRuntimeException(new ObjResult("工作流模型中的表达式错误"));
                    }

                    if ((Boolean)value) {
                        return flow.getTargetFlowElement();
                    }
                } catch (FlowableException var13) {
                    throw new MyRuntimeException(new ObjResult("到《" + flow.getTargetFlowElement().getName() + "》节点的连线错误:" + var13.getCause().getMessage()));
                }
            }

            throw new MyRuntimeException(new ObjResult("工作流模型配置错误，没有找到适合的连线，建议：排他网关后面应该有一条连线没有条件"));
        } else {
            return ((SequenceFlow)outgoingFlows.get(0)).getTargetFlowElement();
        }
    }
}
